home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / oper_sys / quartz / quartz10.lha / src / presto / scheduler.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-02  |  9.3 KB  |  459 lines

  1.  
  2. /*
  3.  * scheduler.c
  4.  *
  5.  *    Implementation of shared scheduler class.  All process
  6.  *    objects interact with the scheduler object trying to
  7.  *    getreadythreads off the ready queue.
  8.  *
  9.  *    ThreadPool trivially defines the base class for the shared
  10.  *    ready thread pool.
  11.  *
  12.  *    ThreadPoolQueue defines the default scheduling discipline.  
  13.  *    Very simple, no priorities.... nothing fancy.
  14.  *
  15.  *      Last modified:          11/07/89
  16.  *      by:                     jef
  17.  *      reason:                 Fix problem when numprocessors > NUMPROCS.
  18.  *                              This caused PRESTO to write right off the
  19.  *                              end of sc_p_numprocs array, trashing
  20.  *                              sc_p_numschedulers field, causing huge
  21.  *                              numbers of processes to be created, using
  22.  *                              up all job slots on system.
  23.  *
  24.  *    Last modified:        1/1/88
  25.  *    by:            bnb
  26.  *    reason:            take care for preschedthreads
  27.  *
  28.  *    Last modified:        12/21/87
  29.  *    by:            bnb
  30.  *    reason:            add these comments
  31.  *
  32.  */
  33.  
  34. #define _SCHEDULER_C
  35.  
  36. #include <sys/types.h>
  37. #include <sys/signal.h>
  38. #include <osfcn.h>
  39. #include <stream.h>
  40. #include "presto.h"
  41.  
  42.  
  43. extern Main *MAIN;
  44.  
  45. //
  46. // preschedthreads queue is where threads that get created
  47. // before the scheduler exists get placed.  On scheduler
  48. // creation, these guys are moved to the real thread pool.
  49. // This allows threads to be created in static constructors without
  50. // requiring that the entire system be in place.
  51. //
  52.  
  53.  
  54. shared_t ThreadQ    *preschedthreads = 0;
  55.  
  56.  
  57. //
  58. // Definition of default ThreadPool{Queue} type known to scheduler.
  59. //
  60.  
  61.  
  62. ThreadPool::ThreadPool()
  63. {
  64. }
  65.  
  66. ThreadPool::~ThreadPool()
  67. {
  68. }
  69.  
  70. Thread*
  71. ThreadPool::get()
  72. {
  73.     error("Base threadpool get");
  74.     // NOTREACHED
  75.     return 0;
  76. }
  77.  
  78. void
  79. ThreadPool::insert(Thread* t)
  80. {
  81.     error("Base threadpool get");
  82.     // NOTREACHED
  83.     t = t;        // Satisfy cfront
  84. }
  85.  
  86. int
  87. ThreadPool::size()
  88. {
  89.     error("Base threadpool size");
  90.     // NOTREACHED
  91.     return 1;
  92. }
  93.  
  94.  
  95.  
  96. ThreadPoolQueue::ThreadPoolQueue()
  97. {
  98.     tp_tq = new ThreadQ(TS_READY);
  99. }
  100.  
  101. ThreadPoolQueue::~ThreadPoolQueue()
  102. {
  103.     delete tp_tq;
  104. }
  105.  
  106.  
  107. Thread*
  108. ThreadPoolQueue::get()
  109. {
  110.     register ThreadQ* tq = tp_tq;        // speed hack
  111.     return tq->get();
  112. }
  113.  
  114. void
  115. ThreadPoolQueue::insert(Thread* t)
  116. {
  117.     register ThreadQ* tq = tp_tq;
  118.     tq->append(t);
  119. }
  120.  
  121. int
  122. ThreadPoolQueue::size()
  123. {
  124.     return tp_tq->length();
  125. }
  126.  
  127.  
  128.  
  129.  
  130. //
  131. // The main scheduler system
  132. //
  133.  
  134. Scheduler::Scheduler(int numschedulers, int quantum = DEFQUANTUM)
  135. {
  136. #ifdef PREEMPT
  137.     void sigpreempt_init();
  138. #endif PREEMPT
  139.  
  140.         //
  141.         //  Make sure user-specified numprocessors isn't more than PRESTO
  142.         //  can support.
  143.         //
  144.         if (numschedulers > NUMPROCS) {
  145.             sc_p_numschedulers = NUMPROCS;
  146.             cerr << "'numprocessors' of " << numschedulers << " too big - ";
  147.             cerr << "using " << NUMPROCS << "\n";
  148.         }
  149.         else
  150.            sc_p_numschedulers = numschedulers;
  151.  
  152. #ifdef DEBUG_STARTUP
  153.         cout << "making scheduler, sc_p_numschedulers = "
  154.              <<sc_p_numschedulers<<"\n";
  155. #endif DEBUG_STARTUP
  156.  
  157.     //
  158.     // nullify the threads
  159.     //
  160.         for (int jj=0; jj<sc_p_numschedulers; jj++)
  161.             sc_t_ready [jj] = new ThreadPoolQueue;
  162.  
  163.     //
  164.     // Only create a process context if one doesn't already exist.
  165.     // User can create his own in Main::init().  Allows us to
  166.     // virtualize the constructor.  
  167.     //
  168.     if (thisproc == 0)
  169.         sc_p_procs[0] = new Process(P_ROOT, 0);
  170.     else
  171.         sc_p_procs[0] = thisproc;
  172.  
  173.     sc_p_activeschedulers = 1;
  174.     sc_p_busybits = 0x0;
  175.     sc_lock = new Spinlock;
  176.     sc_quantum = quantum;
  177.  
  178. #ifdef PREEMPT
  179.     if (sc_quantum)
  180.         sigpreempt_init();
  181. #endif PREEMPT
  182.  
  183. #ifdef DEBUG_STARTUP
  184.     cout << "done making scheduler\n";
  185. #endif DEBUG_STARTUP
  186. }
  187.  
  188. //
  189. // Scheduler invocation function.
  190. //     Create sc_p_numschedulers threads and bind each one of them
  191. //    to a process invocation.  Schedule those threads to start running
  192. //    which will in turn run a new scheduler on some new processor.
  193. //
  194. //    In case we have any preschedthreads, schedule them here.
  195. //
  196.  
  197. int
  198. Scheduler::invoke()
  199. {
  200.     extern int cpus_online();
  201.     extern void strcpy(char*, char*);
  202.     int cpusonline = cpus_online();
  203.     int pid;
  204.  
  205. #ifdef DEBUG_STARTUP
  206.         cout << "scheduler_starter thread - in sched->invoke ()\n";
  207. #endif DEBUG_STARTUP
  208.  
  209. #ifdef PREEMPT
  210.     extern void sigpreempt_beginclock(struct timeval *q);
  211.     extern int preemption_enabled;
  212. #endif PREEMPT
  213.  
  214.     if (sc_p_numschedulers < 0)
  215.         return sc_p_activeschedulers;
  216.  
  217.     if (sc_p_numschedulers > cpusonline)
  218.         sc_p_numschedulers = cpusonline;
  219.  
  220.     //
  221.     // Initialize the process pool
  222.     //
  223.  
  224.     initsighandlers(0);
  225.  
  226.     for (pid = sc_p_activeschedulers; pid < sc_p_numschedulers; pid++) {
  227.  
  228. #define XNAME "proc_X"
  229.         char *pname = new char[sizeof(XNAME)];
  230.         strcpy(pname, XNAME);
  231.         pname[5] = pid + 'a' - 1;    
  232.         sc_p_procs[pid] = thisproc->newprocess(pname,pid+thisproc->id());
  233.         if (sc_p_procs[pid]->state() & S_ERROR)    {
  234.             perror("Scheduler::new Process");
  235.             sc_p_numschedulers = --pid;
  236.             this->abort(SIGKILL);
  237.             kill(getpid(), SIGILL);
  238.             // not reached
  239.         }
  240.         sc_p_busybits |= 1<<pid;    // assume busy
  241.     } 
  242.  
  243.     initsighandlers(1);            // prepare parent to clean up
  244. #ifdef PREEMPT
  245.     if (sc_quantum)    {
  246.         struct timeval    q;
  247.         q.tv_sec = sc_quantum / 1000;        // in msecs
  248.         q.tv_usec = (sc_quantum % 1000) * 1000;    // 1msec = 1000usec
  249.         sigpreempt_beginclock(&q);
  250.     }    
  251. #endif PREEMPT
  252.  
  253.     //
  254.     // take care of threads created in the prescheduler era
  255.     //
  256.  
  257.     if (preschedthreads)    {
  258.         Thread *t;
  259.         while (t=preschedthreads->get())
  260.             resume(t);
  261.         delete preschedthreads;
  262.         preschedthreads = 0;
  263.     }
  264.  
  265. #ifdef DEBUG_STARTUP
  266.         cout << "done with sched->invoke() \n";
  267. #endif DEBUG_STARTUP
  268.  
  269.     return (sc_p_activeschedulers = sc_p_numschedulers);
  270. }
  271.  
  272.  
  273. Scheduler::~Scheduler()
  274. {
  275.     for (int j=0; j<sc_p_numschedulers; j++)
  276.     delete sc_t_ready [j];
  277. }
  278.  
  279.  
  280. void
  281. Scheduler::halt()
  282. {
  283.     int pid;
  284.  
  285. #ifdef PREEMPT
  286.     extern void sigpreempt_stopclock();
  287.  
  288.     if (sc_quantum)
  289.         sigpreempt_stopclock();
  290. #endif PREEMPT
  291. #ifdef PROFILE
  292.     QFinish();
  293. #endif
  294.  
  295.     /*
  296.      * Stop listening for dead children -- otherwise can race with "master"
  297.      * and get spurious "exit" messages from schedulerReapChild().
  298.      */
  299.  
  300.     initsighandlers(-1);                // turn off SIGCHLD
  301.  
  302.     /*
  303.      * Ask the kids to quietly die.
  304.      */
  305.  
  306.     for (pid = 1; pid < sc_p_numschedulers; pid++)
  307.         sc_p_procs[pid]->request( R_RETURN /*R_DIE*/);
  308.  
  309.     sc_p_procs[0]->request(R_RETURN);    // cause main to return
  310. }
  311.  
  312.  
  313.  
  314. //
  315. // Resume a thread within a process.
  316. // If this is a virgin thread, then the thread code starts off a little
  317. // but differently.  See threads.c
  318. //
  319.  
  320. void
  321. Scheduler::resume(Thread *t)
  322. {
  323.     if (t->flags()&TF_SCHEDULER)
  324.         t->error("Can't resume a scheduler thread\n");
  325.     t->isready();
  326.     sc_t_ready [thisproc->id()]->insert(t);    // some process should grab me
  327. }
  328.  
  329.  
  330.  
  331.  
  332. //
  333. // Have to serialize access here so we can know when the system stops.
  334. // Should really be more sophisticated as to determining when everything
  335. // is done.
  336. //
  337.  
  338. Thread*
  339. Scheduler::getreadythread()
  340. {
  341.     Thread *t;
  342.     int id, my_id=thisproc->id();
  343.  
  344.     //
  345.     //  Look in our own readyq first.
  346.     //  If no ready threads in our readyq, try everyone elses.
  347.     //
  348.     t = sc_t_ready [my_id]->get();
  349.  
  350. //if (t) cout << my_id << " - ready thread in my own queue\n";
  351.     id = my_id + 1;
  352.     while (!t)
  353.     {
  354.         if (id == sc_p_numschedulers) id = 0;   // wrap if necessary
  355.         if (id == my_id) break;                 // back where we started
  356.         t = sc_t_ready [id]->get();             // try this process
  357. //if (t) cout << my_id << " - ready thread in " << id << "'s queue\n";
  358.         id++;
  359.     }
  360.  
  361.     if (t)    {
  362. #ifdef PROFILE
  363.             QThreadRun(t->getqThread());
  364. #endif
  365.                 sc_lock->lock();
  366.         (void)busybits(1);
  367.         sc_lock->unlock();
  368.     } else    {
  369.                 sc_lock->lock();
  370.         int busy = busybits(0);
  371.         sc_lock->unlock();        
  372.  
  373. //cout << my_id << " - no ready threads in any queue!!\n";
  374.         //
  375.         // If no busy procs, and we are the master....
  376.         //
  377.         if (busy == 0 && thisproc->isroot() )
  378.             this->halt();
  379.     }
  380.  
  381.     return t;
  382. }
  383.  
  384.  
  385. void
  386. Scheduler::error(char *s)
  387. {
  388.     cerr << "Scheduler error " << s << "\n" << " in " << hex(long(this)) << "\n";
  389.     fatalerror();
  390. }
  391.  
  392.  
  393. ThreadPool*
  394. Scheduler::getreadypool()
  395. {
  396.     return sc_t_ready [thisproc->id()]; 
  397. }
  398.  
  399.  
  400. //
  401. // swap new ready pool.  Order of switch is important here.
  402. //    First, replace old pool with new pool.
  403. //    Then,  move any threads from old pool to new pool
  404. //    Them,  delete old pool.
  405. //
  406.  
  407. ThreadPool*
  408. Scheduler::setreadypool(ThreadPool* newtp)
  409. {
  410.     ThreadPool* oldpool ;
  411.     Thread*    t;
  412.  
  413.     if (!newtp)
  414.         error("Bad arg to newreadypool");
  415.  
  416.     oldpool = sc_t_ready[thisproc->id()];    // can still be active
  417.     sc_t_ready[thisproc->id()] = newtp;
  418.                     // we expect to have only reference to
  419.                     // oldpool by this point.
  420.     if (oldpool)    {
  421.         while (t=oldpool->get())
  422.             sc_t_ready[thisproc->id()]->insert(t);
  423.     }
  424.  
  425.     return oldpool;
  426. }
  427.  
  428. void
  429. Scheduler::print(ostream& s)
  430. {
  431.     int i;
  432.  
  433.     s << form("(Scheduler):0x%x:\n", this);
  434.     for (i = 0; i < sc_p_activeschedulers; i++)
  435.         s << form ("sc_t_ready[%d]=0x%x, ", i, sc_t_ready[i]);
  436.     s << form("sc_p_activescheduler=%d, sc_p_busybits=%d, sc_quantum=%d",
  437.             sc_p_activeschedulers, sc_p_busybits, sc_quantum);
  438.     s << sc_lock << "\n";
  439.     for (i = 0; i < sc_p_activeschedulers; i++)
  440.         s << sc_p_procs[i] << "\n";
  441. }
  442.  
  443. //
  444. // global version of abort.  If we are the scheduler, then knock
  445. // everyone else off first, otherwise just knock ourselves off and
  446. // reply on the scheduler to pick us up.
  447. //
  448. int
  449. abort()
  450. {
  451.     if (sched)    {
  452.         sched->abort(SIGILL);
  453.         // NOT REACHED
  454.     } else            
  455.         kill(getpid(), SIGILL);        // core dump
  456.     return 0;                // not reached
  457. }
  458.  
  459.